﻿//#define DEBUG_COMPARE

using Unity.Collections;
using Unity.Jobs;
using UnityEngine;

public struct Pixel
{
    public bool isIn;
    public float distance;
}

/// <summary>
/// 针对默认实现的Job改造.
/// 一定要开BurstCompile, 调试时关闭.
/// </summary>
public sealed class JobSdfGenerator
{
    public static void GenerateSDF(Texture2D source, Texture2D destination)
    {
        int sourceWidth = source.width;
        int sourceHeight = source.height;
        int targetWidth = destination.width;
        int targetHeight = destination.height;

        //1. 读取像素并生成Pixel结构.
        var sourceColorArray = source.GetRawTextureData<Color32>();
        int sourcePixelCount = sourceWidth * sourceHeight;
        var sourceIsInArray = new NativeArray<bool>(sourcePixelCount, Allocator.Persistent);

        var readJob = new TextureReadJob()
        {
            SourceColors = sourceColorArray,
            SourceIsIn = sourceIsInArray,
        };

        //2. 计算
        int gapX = sourceWidth / targetWidth;
        int gapY = sourceHeight / targetHeight;

        var destinationPixelCount = targetWidth * targetHeight;
        var TargetDistanceArray = new NativeArray<float>(destinationPixelCount, Allocator.Persistent);
        var minDistanceArray = new NativeArray<float>(destinationPixelCount, Allocator.Persistent);
        var maxDistanceArray = new NativeArray<float>(destinationPixelCount, Allocator.Persistent);

        int maxSearchDistance = SdfGenerateConfig.Instance.SearchDistance;

        var calcJob = new SdfCaculateJob()
        {
            sourceWidth = sourceWidth,
            sourceHeight = sourceHeight,
            TexWidth = targetWidth,
            TexHeight = targetHeight,
            SourceIsIn = sourceIsInArray,
            gapX = gapX,
            gapY = gapY,
            TargetDistance = TargetDistanceArray,
            MinDist = minDistanceArray,
            MaxDist = maxDistanceArray,
            MaxSearchDistance = maxSearchDistance,
        };

        //Run
        int batchCount = SdfGenerateConfig.Instance.JobProcessCount;
        var readJobHandle = readJob.Schedule(sourcePixelCount, batchCount);
        var calcJobHandle = calcJob.Schedule(destinationPixelCount, batchCount, readJobHandle);
        var minSortHandle = minDistanceArray.SortJob().Schedule(calcJobHandle);
        var maxSortHandle = maxDistanceArray.SortJob().Schedule(minSortHandle);
        maxSortHandle.Complete();

        //3. 写入
        var minDistance = minDistanceArray[0];
        float clampDist = maxDistanceArray[destinationPixelCount - 1] - minDistance;
        var targetColorArray = new NativeArray<Color>(destinationPixelCount, Allocator.Persistent);
        var writeJob = new WriteTextureJob()
        {
            TargetDistance = TargetDistanceArray,
            TargetColors = targetColorArray,
            minDistance = minDistance,
            clampDist = clampDist,
        };

#if DEBUG_COMPARE
        calcJobHandle.Complete();

        var sourcePixel = DefaultSdfGenerator.ReadSourcePixel(source);

        int counter = 0;
        for (var x = 0; x < sourceWidth; x++)
        {
            for (var y = 0; y < sourceHeight; y++)
            {
                bool defaultIn = sourcePixel[x, y].isIn;
                bool jobIn = sourceIsInArray[Helper.XYToIndex(x, y, sourceWidth)];
                if(defaultIn != jobIn)
                {
                    //Debug.LogWarning(x + "_" + y);
                    counter++;
                }
            }
        }
        Debug.LogWarning("source in diff counter: " + counter);

        //比较TargetPixelArray和DefaultGenerator的值
        var (targetPixels, max_distance, min_distance) = DefaultSdfGenerator. CalcTargetPixel(source, destination, sourcePixel);

        counter = 0;
        for (var x = 0; x < targetWidth; x++)
        {
            for (var y = 0; y < targetHeight; y++)
            {
                int index = Helper.XYToIndex(x, y, targetWidth);

                if(targetPixels[x,y].distance != TargetDistanceArray[index])
                {
                    counter++;
                }
            }
        }
        Debug.LogWarning("pixel diff counter: " + counter);
        Debug.Log($"max diff:{maxDistanceArray[destinationPixelCount - 1] - max_distance}.min diff:{minDistance - min_distance}");

#else

        writeJob.Schedule(destinationPixelCount, batchCount).Complete();

        //Apply to texture.
        for (int x = 0; x < targetWidth; x++)
        {
            for (int y = 0; y < targetHeight; y++)
            {
                //var color = targetColorArray[y * targetWidth + x];
                //destination.SetPixel(y, x, color);
                var color = targetColorArray[Helper.XYToIndex(x, y, targetWidth)];
                destination.SetPixel(x, y, color);
            }
        }
        destination.Apply();
#endif

        //Dispose
        sourceColorArray.Dispose();
        sourceIsInArray.Dispose();
        TargetDistanceArray.Dispose();
        targetColorArray.Dispose();
        minDistanceArray.Dispose();
        maxDistanceArray.Dispose();
    }
}